/* Hybrid System Abstractor
 *
 * Copyright (c) 2013, Fortiss GmbH - An-Institut TU Muenchen
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fortiss.util;

import javax.xml.bind.annotation.*;

public class Matrix {
    @XmlAttribute
    private int row;
    @XmlAttribute
    private int column;
    
    @XmlElement
    private ListWrapper<ListWrapper<Double>> matrix;
        
    public Matrix()     {}
    
    public Matrix(double m[][])
    {
        row = m.length;
        column = m.length>0 ? m[0].length : 0;
                
        matrix = new ListWrapper<ListWrapper<Double>>(m.length);
        for (int i=0; i<m.length; i++){
            matrix.add(new ListWrapper<Double>(m[i].length));
            for (int j=0; j<m[i].length; j++)
                matrix.get(i).add(m[i][j]);
        }
    }
    
    private Matrix(int r, int c)
    {
        this.row = r;
        this.column = c;
        matrix = new ListWrapper<ListWrapper<Double>>(r);
        for (int i=0; i<r; i++){
            matrix.add(new ListWrapper<Double>(c));
            for (int j=0; j<c; j++)
                matrix.get(i).add(null);
        }
    }
    
    public int getRow()    { return this.row; }
    public int getColumn() { return this.column; }
    
    public double getElement(int i, int j)
    {
        return matrix.get(i).get(j);
    }
    
    private void setElement(int r, int c, double i)
    {
        matrix.get(r).set(c, i);
    }
    
    public Matrix times(double d)
    {
        Matrix ret = new Matrix(this.getRow(), this.getColumn());
        for (int i=0; i<ret.getRow(); i++)
            for (int j=0; j<ret.getColumn(); j++)
                ret.setElement(i, j, d*this.getElement(i, j));
        return ret;
    }
    
    public Matrix times(Matrix m)
    {
        Matrix ret = new Matrix(this.getRow(), m.getColumn());
        
        for (int i=0; i<this.getRow(); i++)
            for (int j=0; j<m.getColumn(); j++)
                for (int k=0; k<this.getColumn(); k++)
                    ret.setElement(i, j, this.getElement(i, k)*m.getElement(k, j));
        return ret;
    }
    
    public IntervalMatrix times(IntervalMatrix im)
    {
        double m[][] = new double[this.getRow()][];
        for (int i=0; i<this.getRow(); i++){
            m[i] = new double[this.getColumn()];
            for (int j=0; j<this.getColumn(); j++)
                m[i][j] = this.getElement(i, j);
        }
        return new IntervalMatrix(m).times(im);
    }
    
    public Matrix addCoefficient(Matrix m)
    {
        Matrix ret = new Matrix(this.getRow(), this.getColumn());

        for (int i=0; i<this.getRow(); i++){
            for (int j=0; j<this.getColumn(); j++)
                ret.setElement(i, j, this.getElement(i, j));
            ret.setElement(i, this.getColumn(), m.getElement(i, 0));
        }
        return ret;
    }
    
    public Matrix getCoefficient()
    {
        Matrix ret = new Matrix(this.getRow(), 1);
        for (int i=0; i<this.getRow(); i++)
            ret.setElement(i, 0, this.getElement(i, this.getColumn()-1));
        return ret;
    }
    
    public Matrix delCoefficient()
    {
        Matrix ret = new Matrix();
        ret.matrix = this.matrix;
        ret.row    = this.row;
        ret.column = this.column-1;
        return ret;
    }

    @Override
    public boolean equals(Object o)
    {
        if (o instanceof Matrix){
            Matrix m = (Matrix) o;
            if (this.getRow()!=m.getRow())          return false;
            if (this.getColumn()!=m.getColumn())    return false;
            for (int i=0; i<this.getRow(); i++)
                for (int j=0; j<this.getColumn(); j++)
                    if (this.getElement(i, j)!=m.getElement(i, j))
                        return false;
            return true;
        }
        else
            return false;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + this.row;
        hash = 89 * hash + this.column;
        hash = 89 * hash + (this.matrix != null ? this.matrix.hashCode() : 0);
        return hash;
    }
    
    public void println()
    {
        System.out.format("[");
        for (int i=0; i<this.getRow(); i++){
            for (int j=0; j<this.getColumn(); j++)
                System.out.print(" " + this.getElement(i, j));
            System.out.format("; ");
        }
        System.out.format("]\n");
    }
}
